﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Reflection;
using System.IO;

namespace Pixeldyne.Core.Configuration
{
	/// <summary>
	/// Command line arguments handler. Valid parameter forms:
	/// {-,/,--}param{ ,=,:}((",')value(",'))
	/// Examples: -param1 value1 --param2 /param3:"Test-:-work" /param4=happy -param5 '--=nice=--'
	/// </summary>
	[Serializable]
	public class CommandOptions
	{
		readonly Regex splitter = new Regex(@"^-{1,2}|^/|=|:(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
		readonly Regex remover = new Regex(@"^['""]?(.*?)['""]?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		readonly StringDictionary parameters = new StringDictionary();

		/// <summary>
		/// Gets the name of the executable file name.
		/// </summary>
		/// <value>The executable file name.</value>
		public static string Executable
		{
			get
			{
				return Path.GetFileName(Assembly.GetExecutingAssembly().Location);
			}
		}

		/// <summary>
		/// Initialises a new instance of the <see cref="CommandOptions"/> class.
		/// </summary>
		/// <param name="parameters">The parameters.</param>
		public CommandOptions(StringDictionary parameters)
		{
			this.parameters = parameters;
		}

		/// <summary>
		/// Initialises a new instance of the <see cref="CommandOptions"/> class.
		/// </summary>
		/// <param name="args">The args.</param>
		public CommandOptions(IEnumerable<string> args)
		{
			string parameter = null;
			string[] parts;

			foreach (string text in args)
			{
				// Look for new parameters (-,/ or --) and a possible enclosed value (=,:)
				parts = splitter.Split(text, 3);

				switch (parts.Length)
				{
					// Found a value (for the last parameter found (space separator))
					case 1:
						if (parameter != null)
						{
							if (!parameters.ContainsKey(parameter))
							{
								parts[0] = remover.Replace(parts[0], "$1");
								parameters.Add(parameter, parts[0]);
							}

							parameter = null;
						}
						// else Error: no parameter waiting for a value (skipped)
						break;
					// Found just a parameter
					case 2:
						// The last parameter is still waiting. With no value, set it to true.
						if (parameter != null)
						{
							if (!parameters.ContainsKey(parameter))
								parameters.Add(parameter, "true");
						}

						parameter = parts[1];

						break;
					// Parameter with enclosed value
					case 3:
						// The last parameter is still waiting. With no value, set it to true.
						if (parameter != null)
						{
							if (!parameters.ContainsKey(parameter))
								parameters.Add(parameter, "true");
						}

						parameter = parts[1];

						// Remove possible enclosing characters (",')
						if (!parameters.ContainsKey(parameter))
						{
							parts[2] = remover.Replace(parts[2], "$1");
							parameters.Add(parameter, parts[2]);
						}

						parameter = null;

						break;
				}
			}

			if (parameter == null) return;

			// In case a parameter is still waiting
			if (!parameters.ContainsKey(parameter)) parameters.Add(parameter, "true");
		}

		/// <summary>
		/// Retrieves a parameter value if it exists.
		/// </summary>
		/// <value></value>
		public string this[string parameter]
		{
			get
			{
				return parameters[parameter];
			}
		}
	}
}
